<template>
  <div @dragover="handleTargetDragOver" @dragleave="dragleave">
    <div id="view-menu">
      <div class="icon-container">
        <span class="icon-span" title="清空" @click="refresh">
          <i class="el-icon-refresh" style="color: #fff; font-size: 16px"></i>
        </span>
        <span class="icon-span" title="布局">
          <span aria-label="node-index" class="anticon anticon-node-index" role="img" style="color: rgb(255, 255, 255)">
            <el-dropdown trigger="click" @command="updateLayout">
              <span class="el-dropdown-link">
                <i class="el-icon-sort" style="color: rgb(255, 255, 255); font-size: 16px"></i>
              </span>
              <el-dropdown-menu slot="dropdown">
                <el-dropdown-item :command="'fruchterman'">FR算法布局</el-dropdown-item>
                <el-dropdown-item :command="'circular'">环形布局</el-dropdown-item>
                <el-dropdown-item :command="'force'">经典力导向布局</el-dropdown-item>
                <el-dropdown-item :command="'concentric'">同心圆布局</el-dropdown-item>
                <el-dropdown-item :command="'radial'">辐射状布局</el-dropdown-item>
                <el-dropdown-item :command="'mds'">高维数据降维算法布局</el-dropdown-item>
                <el-dropdown-item :command="'grid'">格子布局</el-dropdown-item>
              </el-dropdown-menu>
            </el-dropdown>
          </span>
        </span>
        <span v-show="nodeId.length > 1" class="icon-span" title="关系" @click="editRelation()">
          <span aria-label="node-index" class="anticon anticon-node-index" role="img" style="color: rgb(255, 255, 255)">
            <i class="el-icon-share" style="font-size: 16px"></i>
          </span>
        </span>
        <span class="icon-span" title="删除" v-if="selEdges.length > 0" @click="del()">
          <span aria-label="node-index" class="anticon anticon-node-index" role="img" style="color: rgb(255, 255, 255)">
            <i class="el-icon-delete" style="font-size: 16px"></i>
          </span>
        </span>
        <span>
          <el-button type="text" @click="searchResult">查 询</el-button>
          <!-- <span style="font-size:16px;color:#fff;"  @click="searchNode()">查 询</span> -->
        </span>
      </div>
    </div>
    <div id="subnet" v-loading="isLoading"></div>
  </div>
</template>

<script>
  import G6 from "@antv/g6";

  export default {
    data() {
      return {
        graph: null,
        subNodeStyle: {
          fill: "#73e6ab",
          stroke: "#34ba76",
          lineWidth: 2,
        },
        attrNodeStyle: {
          fill: "#fc8af7",
          stroke: "#fe16fd",
          lineWidth: 2,
        },
        subNodeSelStyle: {
          fill: "#34ba76",
          stroke: "#34ba76",
          lineWidth: 2,
        },
        nodes: {}, //节点集合
        edges: {}, //边集合
        nodeId: [], //拖入的节点id集合
        selNodes: [], //选择节点集合
        isDbclickArr: [], //双击节点集合
        okBind: {},
        isLoading: false, //是否加载
        startNode: "",
        addRelation: {}, //选择关系
        isDragDown: false,
        selEdges: [], //选中的边集合
      };
    },
    mounted() {
      this.initView();
    },
    methods: {
      //初始化视图
      initView() {
        let _this = this;
        const defaultConf = {
          labelCfg: {
            autoRotate: true,
            style: {
              fill: '#1890ff',
              fontSize: 14,
              background: {
                fill: '#ffffff',
                stroke: '#9EC9FF',
                padding: [2, 2, 2, 2],
                radius: 2,
              },
            },
          },
          style: {
            stroke: '#1890ff',
            lineAppendWidth: 20,
            endArrow: {
              path: G6.Arrow.triangle(10, 10, 10), // 使用内置箭头路径函数，参数为箭头的 宽度、长度、偏移量（默认为 0，与 d 对应）
              d: 15,
            },
          },
          /**
           * 绘制边
           * @override
           * @param  {Object} cfg   边的配置项
           * @param  {G.Group} group 边的容器
           * @return {G.Shape} 图形
           */
          drawShape(cfg, group) {
            const item = group.get('item')
            const shapeStyle = this.getShapeStyle(cfg, item);
            console.log(shapeStyle);
            const shape = group.addShape('path', {
              attrs: shapeStyle
            });
            return shape;
          },
          drawLabel(cfg, group) {
            const labelCfg = cfg.labelCfg || {}
            const labelStyle = this.getLabelStyle(cfg, labelCfg, group)
            // console.log(labelStyle);
            const text = group.addShape('text', {
              attrs: {
                ...labelStyle,
                text: cfg.label,
                fontSize: 12,
                cursor: 'pointer'
              },
              className: 'edge-label'
            })
            return text
          },

          /**
           * 获取图形的配置项
           * @internal 仅在定义这一类节点使用，用户创建和更新节点
           * @param  {Object} cfg 节点的配置项
           * @return {Object} 图形的配置项
           */
          getShapeStyle(cfg, item) {
            const {
              startPoint,
              endPoint
            } = cfg
            const type = item.get('type')

            const defaultStyle = this.getStateStyle('default', true, item)
            if (type === 'node') {
              return Object.assign({}, cfg.style, defaultStyle);
            }
            const controlPoints = this.getControlPoints(cfg);
            let points = [startPoint]; // 添加起始点
            // 添加控制点
            if (controlPoints) {
              points = points.concat(controlPoints);
            }
            // 添加结束点
            points.push(endPoint);
            const path = this.getPath(points);

            const style = Object.assign({}, {
              path
            }, cfg.style, defaultStyle);
            return style;
          },
          getControlPoints(cfg) {
            let controlPoints = cfg.controlPoints; // 指定controlPoints
            if (!controlPoints || !controlPoints.length) {
              const {
                startPoint,
                endPoint
              } = cfg;
              const innerPoint = G6.Util.getControlPoint(startPoint, endPoint, 0.5, cfg.edgeOffset ||
                0);
              // console.log(innerPoint);
              controlPoints = [innerPoint];
            }
            return controlPoints;
          },
          /**
           * 获取三次贝塞尔曲线的path
           *
           * @param {array} points 起始点和两个控制点
           * @returns
           */
          getPath(points) {
            const path = [];
            path.push(['M', points[0].x, points[0].y]);
            path.push(['Q', points[1].x, points[1].y, points[2].x, points[2].y]);
            return path;
          },
          /**
           * 根据不同状态，获取不同状态下的样式值
           * @param {string} name 
           * @param {string} value 
           * @param {Item} item 
           */
          getStateStyle(name, value, item) {
            const model = item.getModel();
            const {
              style = {}
            } = model

            const defaultStyle = Object.assign({}, this.style)
            // 更新颜色
            return {
              ...defaultStyle,
              lineWidth: 1,
              ...style
            }
          },
          /**
           * 拖动时更新path及边的label
           *
           * @param {object} cfg 边的model
           * @param {Edge} item 边的实例
           */
          update(cfg, item) {
            const {
              g6data,
              style,
              startPoint,
              endPoint,
              labelCfg = {}
            } = cfg
            const group = item.getContainer()
            const model = g6data || cfg

            const defaultStyle = Object.assign({}, this.style, {
              lineWidth: 1,
            }, style)

            const {
              opacity,
              onlyHideText
            } = defaultStyle

            // 更新 path
            const keyShape = item.getKeyShape();
            const controlPoints = this.getControlPoints(cfg);

            keyShape.attr({
              path: [
                ['M', startPoint.x, startPoint.y],
                ['Q', controlPoints[0].x, controlPoints[0].y, endPoint.x, endPoint.y]
              ],
              ...defaultStyle
            });

            const labelStyle = this.getLabelStyle(cfg, labelCfg, group);
            const text = group.findByClassName('edge-label');
            const attrs = {
              ...labelStyle,
              fillOpacity: onlyHideText ? 0 : opacity === 0 ? opacity : 1,
              fill: '#5F95FF',
            }
            if (text) {
              text.resetMatrix();
              text.attr(attrs);
            }
          }
        };
        G6.registerEdge('quadratic-label-edge', defaultConf, 'quadratic');
        const container = document.getElementById("subnet");
        const width = container.scrollWidth;
        const height = container.scrollHeight || 500;
        this.graph = new G6.Graph({
          container: "subnet",
          width,
          height,
          animate: true, // Boolean，切换布局时是否使用动画过度，默认为 false
          modes: {
            default: [
              "drag-canvas",
              "drag-node",
              "zoom-canvas",
              {
                type: "click-select",
                trigger: "ctrl",
                // 是否允许该 behavior 发生。若返回 false，被操作的 item 不会被选中，也不会触发 'nodeselectchange' 时机事件
                shouldBegin: (e) => {
                  return true;
                },
                shouldUpdate: (e) => {
                  if (
                    e.item._cfg.model.name === "Thing" ||
                    e.item._cfg.model.class == "attr"
                  )
                    return false;
                  return true;
                },
              },
            ],
            relation: [
              "drag-canvas",
              "zoom-canvas",
              "click-select",
              {
                type: "create-edge",
                trigger: "drag", // 'click' by default. options: 'drag', 'click'
                shouldBegin: (e) => {
                  this.startNode = e.item._cfg.id;
                  return true;
                },
                shouldEnd: (e) => {
                  if (this.startNode === e.item._cfg.model.id) return false;
                  let edges = _this.graph.findById(this.startNode)._cfg.edges
                  for (const item of edges) {
                    if (item._cfg.model.edgeOffset !== undefined) {
                      console.log(item._cfg);
                      if (item._cfg.model.label == this.addRelation.label_name && this.startNode == item._cfg
                        .sourceNode._cfg.id) {
                        return false
                      }
                    }
                  }
                  return true;
                },
              },
            ],
            edit: [
              "click-select",
              {
                type: "brush-select",
                // trigger: 'ctrl',
                includeEdges: false,
                // 是否允许对该 behavior 发生。若返回 false，被操作的 item 不会被选中，不触发 'nodeselectchange' 时机事件
                brushStyle: {
                  fill: "#3333ff",
                  fillOpacity: 0.2,
                  stroke: "#5e7cff",
                  lineWidth: 2,
                },
              },
            ],
          },
          layout: {
            type: "fruchterman",
            gravity: 5,
            speed: 5,
          },
          defaultNode: {
            type: "circle", // 节点类型
            size: 60,
            style: {
              fill: "#69bef7",
              stroke: "#017bff",
              lineWidth: 2,
            },
            labelCfg: {
              style: {
                fill: "#000",
              },
            },
          },
          defaultEdge: {
            type: 'quadratic-label-edge',
            // type: "quadratic",
            labelCfg: {
              autoRotate: true,
              position: 'middle',
              style: {
                fill: "#0000CD",
              },
              background: {
                fill: '#ffffff',
                stroke: '#9EC9FF',
                padding: [2, 2, 2, 2],
                radius: 2,
              },
            },
            // style: {
            //   lineAppendWidth: 20,
            //   endArrow: {
            //     path: G6.Arrow.triangle(10, 10, 15), // 使用内置箭头路径函数，参数为箭头的 宽度、长度、偏移量（默认为 0，与 d 对应）
            //     d: 25,
            //   },
            // },
          },
          nodeStateStyles: {
            selected: {
              fill: "#e6a23c",
              stroke: "#b58134",
              lineWidth: 2,
            },
            checked: {
              fill: "#e6a23c",
              stroke: "#b58134",
              lineWidth: 2,
            },
          },
          edgeStateStyles: {
            labelCfg: {
              style: {
                fill: "#0000CD",
              },
            },
            select: {
              fill: "#e6a23c",
              lineWidth: 2,
              shadowBlur: 10,
              shadowColor: "rgb(95, 149, 255)",
              stroke: "rgb(95, 149, 255)",
            },
          },
        });
        //绑定事件
        this.graph.on("nodeselectchange", (ev) => {
          // console.log(ev);
          const select = ev.select;
          if (select) {
            //判断多选还是单选
            //存在是单选
            if (ev.target) {
              this.$emit("TouchWindow", true, ev.target._cfg.model);
            }
            this.graph.setMode("edit"); //进入编辑模式
          } else {
            this.$emit("openRelationPop", false);
            this.graph.setMode("default"); //进入默认模式
            this.$emit("TouchWindow", false, null);
            this.graph.render();
          }
          //选中状态
        });
        this.graph.on("aftercreateedge", (ev) => {
          // console.log(ev);
          const sourceModel = ev.edge.getSource().getModel();
          const targetModel = ev.edge.getTarget().getModel();
          const startEdgs = _this.graph.findById(sourceModel.id)._cfg.edges;
          //解决两节点多重边重叠
          let offsetNum = 0;
          if (startEdgs.length > 1) {
            let lastNode = startEdgs[startEdgs.length - 2]._cfg.model;
            offsetNum = Number(lastNode.edgeOffset);
            if (lastNode.edgeOffset > 0) {
              offsetNum = Number(lastNode.edgeOffset);
              offsetNum -= (startEdgs.length - 1) * 30
            } else {
              offsetNum += (startEdgs.length - 1) * 30

            }
          }
          this.edges[(
            Number(this.nodeId[this.nodeId.length - 1]) + 1 + Object.keys(this.edges).length
          ).toString() + '_' + this.addRelation.id] = {
            relationid: this.addRelation.id,
            source: sourceModel.id,
            target: targetModel.id,
            name: this.addRelation.label_name,
            label: this.addRelation.label_name,
            edgeOffset: offsetNum,
            id: (
              Number(this.nodeId[this.nodeId.length - 1]) + 1 + Object.keys(this.edges).length
            ).toString() + '_' + this.addRelation.id,
          };
          this.graph.data({
            nodes: Object.values(this.nodes),
            edges: Object.values(this.edges),
          });
          this.graph.render();
        });
        this.graph.on("edge:click", (ev) => {
          // console.log(ev);
          const {
            item
          } = ev;
          // console.log(this.edges);
          if (this.selEdges.length == 0) {
            this.graph.setItemState(item, "selected", true);
            this.selEdges.push(item);
            return;
          } else {
            this.selEdges.forEach((edge) => {
              if (edge._cfg) {
                this.graph.setItemState(edge, "selected", false);
              }
            });
          }
          this.selEdges = [];
          this.graph.setItemState(item, "selected", true);
          this.selEdges.push(item);
        });
      },
      //添加模型
      findApointIdNode(node) {
        console.log(node);
        let addSubNode = {};
        if (this.nodeId.length == 0) {
          addSubNode.id = "1";
        } else {
          addSubNode.id = (
            Number(this.nodeId[this.nodeId.length - 1]) + 1
          ).toString();
        }
        this.nodeId.push(addSubNode.id);
        addSubNode.class = "subnode";
        addSubNode.name = node.label_name;
        addSubNode.label = node.label_name + addSubNode.id;
        addSubNode.yuan = node.label_name + addSubNode.id;

        addSubNode.style = this.subNodeStyle;
        addSubNode.stateStyles = {
          selected: this.subNodeSelStyle,
          checked: this.subNodeSelStyle,
        };
        this.nodes[addSubNode.id] = addSubNode;
        this.graph.data({
          nodes: Object.values(this.nodes),
          edges: Object.values(this.edges),
        });
        this.graph.render();

      },
      //重新刷新
      refresh() {
        console.log("清空");
        this.graph.destroy();
        this.nodes = [];
        this.edges = [];
        this.nodeId = [];

        this.isDbclickArr = []; //清空已经炸开属性和子节点的节点集合
        this.$emit("openRelationPop", false);
        this.initView();
      },
      //鼠标悬浮在g6界面 //进入可释放区域
      handleTargetDragOver(e) {
        this.isDragDown = true;
        console.log(this.isDragDown);
        e.preventDefault(); // 使该区域允许释放
      }, //离开可以释放区域
      dragleave() {
        this.isDragDown = false;
      },
      //切换布局
      updateLayout(type) {
        let layout = {};
        if (type == "fruchterman") {
          layout = {
            type: "fruchterman",
            gravity: 5,
            speed: 5,
          };
        } else if (type == "circular") {
          layout = {
            type: "circular",
            radius: 200,
          };
        } else if (type == "grid") {
          layout = {
            type: "grid",
          };
        } else if (type == "force") {
          layout = {
            type: "force",
            preventOverlap: true,
            nodeSize: 60,
            linkDistance: 150,
          };
        } else if (type == "radial") {
          layout = {
            type: "radial",
            preventOverlap: true,
            nodeSize: 60,
            linkDistance: 100,
          };
        } else if (type == "concentric") {
          layout = {
            type: "concentric",
            nodeSize: 60,
            minNodeSpacing: 100,
            preventOverlap: true,
          };
        } else if (type == "mds") {
          layout = {
            type: "mds",
            linkDistance: 250,
          };
        }

        this.graph.updateLayout(layout);
        this.graph.render();
      },
      //编辑关系
      editRelation() {
        //弹出关系视图
        this.$emit("openRelationPop", true);
      },
      //删除节点或者边
      del() {
        if (this.selEdges.length > 0) {
          for (let i in this.selEdges) {
            this.delEdge(this.selEdges[i], i);
          }
        } else {
          this.selNodes.forEach((item) => {
            this.delNode(item);
          });
          this.selNodes = [];
        }
      },
      //删除边
      delEdge(item, i) {
        // console.log(item.getModel().id);
        //继承关系不能删除
        if (item._cfg.model.name == "继承") {
          this.graph.setItemState(item, "selected", false);
          this.$message({
            message: "不能删除继承",
            type: "warning",
          });
          return;
        }
        delete this.edges[item._cfg.id]
        this.selEdges.splice(i, 1);
        this.graph.removeItem(item);
      },
      //进入编辑关系模式
      entryRelation(data) {
        if (data) {
          this.addRelation = data;
          this.graph.setMode("relation");
        }
      },
      //进入默认模式
      entrydefault() {
        this.graph.setMode("default");
      },
      //获取结果
      searchResult() {
        let nodelist = [];
        let edgelist = [];
        console.log(this.nodes);
        console.log(this.edges);
        for (let item in this.nodes) {
          console.log(this.nodes[item]);
          let obj = {};
          obj.label_name = this.nodes[item].name;
          obj.node_no = Number(this.nodes[item].id);
          nodelist.push(obj);
        }
        for (let s in this.edges) {
          console.log(this.edges[s]);
          let obj = {};
          obj.relationship_label_name = this.edges[s].name;
          obj.relationship_no = Number(this.edges[s].id);
          obj.start_node_no = Number(this.edges[s].source);
          obj.end_node_no = Number(this.edges[s].target);
          edgelist.push(obj);
        }
        console.log(nodelist, edgelist);
        this.$emit("firingResult", nodelist, edgelist);
      },
      //搜索当前节点
      searchNode() {
        const value = document.getElementById("search-node-input").value;
        const node = this.graph.find("node", (node) => {
          return node.get("model").label.includes(value);
        });
        if (node) {
          this.graph.focusItem(node, true);
          this.graph.setItemState(node, "selected", true);
        } else {
          this.$message.warning("没有找到该节点");
        }
      },
    },
  };
</script>

<style lang="scss" scoped>
  #subnet {
    width: 100%;
    height: 100%;
  }

  #view-menu {
    position: absolute;
    /*display: none;*/
    z-index: 2200;
    right: 15px;
    top: 42px;
    width: fit-content;
    padding: 8px 16px;
    background-color: rgba(54, 59, 64, 1);
    border-radius: 24px;
    box-shadow: 0 5px 18px 0 rgba(0, 0, 0, 0);
    font-family: PingFangSC-Semibold;
    transition: all 0.2s linear;
  }

  #view-menu:hover {
    background-color: rgba(54, 59, 64, 1);
    box-shadow: 0 5px 18px 0 rgba(0, 0, 0, 0.6);
  }

  .icon-span {
    padding-left: 8px;
    padding-right: 8px;
    cursor: pointer;
  }

  #search-node-input {
    background-color: rgba(60, 60, 60, 0.95);
    border-radius: 21px;
    width: 100px;
    border-color: rgba(80, 80, 80, 0.95);
    border-style: solid;
    color: rgba(255, 255, 255, 0.85);
  }

  #submit-button {
    background-color: rgba(82, 115, 224, 0.2);
    border-radius: 21px;
    border-color: rgb(82, 115, 224);
    border-style: solid;
    color: rgba(152, 165, 254, 1);
    margin-left: 4px;
  }
</style>